#include "stdafx.h"
#include "utils/sharedlibrary.hpp"
#include "filesys/path.hpp"
using namespace JHCPP::filesys;
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib::jhException;

namespace JHCPP
{
	namespace utils
	{
	#if defined(JH_OS_WINDOWS)
		CMutex CSharedLibraryImpl::m_mutex;

		CSharedLibraryImpl::CSharedLibraryImpl()
		{
			m_handle = 0;
		}

		CSharedLibraryImpl::~CSharedLibraryImpl()
		{
		}

		void CSharedLibraryImpl::loadImpl(const std::string& path, int /*flags*/)
		{
			CMutex::ScopedLock lock(m_mutex);

			if (m_handle) throw LibraryAlreadyLoadedException(m_path);
			DWORD flags(0);
			CPath p(path);
			if (p.isAbsolute()) flags |= LOAD_WITH_ALTERED_SEARCH_PATH;
			m_handle = ::LoadLibraryEx(path.c_str(), 0, flags);
			if (!m_handle) throw LibraryLoadException(path);
			m_path = path;
		}

		void CSharedLibraryImpl::unloadImpl()
		{
			CMutex::ScopedLock lock(m_mutex);

			if (m_handle)
			{
				::FreeLibrary((HMODULE) m_handle);
				m_handle = 0;
			}
			m_path.clear();
		}

		bool CSharedLibraryImpl::isLoadedImpl() const
		{
			return m_handle != 0; 
		}

		void* CSharedLibraryImpl::findSymbolImpl(const std::string& name)
		{
			CMutex::ScopedLock lock(m_mutex);

			if (m_handle)
			{
				return (void*) GetProcAddress((HMODULE) m_handle, name.c_str());
			}
			else 
				return 0;
		}

		const std::string& CSharedLibraryImpl::getPathImpl() const
		{
			return m_path;
		}

		std::string CSharedLibraryImpl::suffixImpl()
		{
		#if defined(_DEBUG)
			return "d.dll";
		#else
			return ".dll";
		#endif
		}
	#elif defined(JH_OS_LINUX)
#include <dlfcn.h>
		CMutex CSharedLibraryImpl::m_mutex;

		CSharedLibraryImpl::CSharedLibraryImpl()
		{
			m_handle = 0;
		}

		CSharedLibraryImpl::~CSharedLibraryImpl()
		{
		}

		void CSharedLibraryImpl::loadImpl(const std::string& path, int flags)
		{
			CMutex::ScopedLock lock(m_mutex);

			if (m_handle) throw LibraryAlreadyLoadedException(path);
			int realFlags = RTLD_LAZY;
			if (flags & SHLIB_LOCAL_IMPL)
				realFlags |= RTLD_LOCAL;
			else
				realFlags |= RTLD_GLOBAL;
			m_handle = dlopen(path.c_str(), realFlags);
			if (!m_handle)
			{
				const char* err = dlerror();
				throw LibraryLoadException(err ? std::string(err) : path);
			}
			m_path = path;
		}

		void CSharedLibraryImpl::unloadImpl()
		{
			CMutex::ScopedLock lock(m_mutex);

			if (m_handle)
			{
				dlclose(m_handle);
				m_handle = 0;
			}
		}

		bool CSharedLibraryImpl::isLoadedImpl() const
		{
			return m_handle != 0; 
		}

		void* CSharedLibraryImpl::findSymbolImpl(const std::string& name)
		{
			CMutex::ScopedLock lock(m_mutex);

			void* result = 0;
			if (m_handle)
			{
				result = dlsym(m_handle, name.c_str());
			}
			return result;
		}

		const std::string& CSharedLibraryImpl::getPathImpl() const
		{
			return m_path;
		}

		std::string CSharedLibraryImpl::suffixImpl()
		{
		#if defined(__CYGWIN__)
		#if defined(_DEBUG)
					return "d.dll";
		#else
					return ".dll";
		#endif
		#else
		#if defined(_DEBUG)
					return "d.so";
		#else
					return ".so";
		#endif
		#endif
		}
	#endif

		////////////////////////////////////////////////////////////////////
		//CSharedLibrary
		////////////////////////////////////////////////////////////////////
		CSharedLibrary::CSharedLibrary()
		{
		}

		CSharedLibrary::CSharedLibrary(const std::string& path)
		{
			loadImpl(path, 0);
		}

		CSharedLibrary::CSharedLibrary(const std::string& path, int flags)
		{
			loadImpl(path, flags);
		}

		CSharedLibrary::~CSharedLibrary()
		{
		}

		void CSharedLibrary::load(const std::string& path)
		{
			loadImpl(path, 0);
		}

		void CSharedLibrary::load(const std::string& path, int flags)
		{
			loadImpl(path, flags);
		}

		void CSharedLibrary::unload()
		{
			unloadImpl();
		}

		bool CSharedLibrary::isLoaded() const
		{
			return isLoadedImpl();
		}

		bool CSharedLibrary::hasSymbol(const std::string& name)
		{
			return findSymbolImpl(name) != 0;
		}

		void* CSharedLibrary::getSymbol(const std::string& name)
		{
			void* result = findSymbolImpl(name);
			if (result)
				return result;
			else
				throw NotFoundException(name);
		}

		const std::string& CSharedLibrary::getPath() const
		{
			return getPathImpl();
		}

		std::string CSharedLibrary::suffix()
		{
			return suffixImpl();
		}
	}//end of namespace utils
}//end of namespace JHCPP
